#include "../include/CRenderEnemy.h"
#include "../include/CDefaultEnemyBehaviour.h"
#include "../include/CDefaultShotBehaviour.h"
#include "../include/CDefaultPowerUpBehaviour.h"
#include "../include/CRenderPlayer.h"
#include "../include/CPlayerBehaviour.h"
#include "../include/CSprite.h"
#include "../include/CSpriteAnimation.h"

#include <SDL/SDL.h>

//**************************
//Description : Constructor
//Parameters : None
//Note : None
//**************************
CRenderEnemy::CRenderEnemy()
    : IRenderEntity(),
    m_bHasPowerUp(false)
{
    m_EnemyEntity = NULL;
    m_PowerUpEntity = NULL;
}

//**************************
//Description : Constructor
//Parameters : The screen,
//                Animations for the enemy and its shots
//                A pointer toward an enemy
//             its time before appearance
//                The PowerUp Behaviour (if any) and the animation for the power up (if any)
//Note : None
//**************************
CRenderEnemy::CRenderEnemy(SDL_Surface * _screen,
                        CDefaultEnemyBehaviour * _Enemy, long _lAppearance,
                        CSpriteAnimation * _EnemyAnimations,
                        CSpriteAnimation * _EnemyShotsAnimation,
                        CDefaultPowerUpBehaviour * _PowerUp,
                        CSpriteAnimation * _PowerUpAnimation)
    : IRenderEntity(_screen),
    m_bHasPowerUp(false)
{
    SetSprites(_EnemyAnimations, _EnemyShotsAnimation);

    SetEnemyBehaviour(_Enemy, _lAppearance);

    SetPowerUpBehaviour(_PowerUp, _PowerUpAnimation);
}

//**************************
//Description : Copy Constructor
//Parameters : A CRenderEnemy
//Note : None
//**************************
CRenderEnemy::CRenderEnemy(CRenderEnemy & _RenderEnemy)
    : IRenderEntity(_RenderEnemy),
    m_bHasPowerUp(_RenderEnemy.m_bHasPowerUp),
    m_iInitX(_RenderEnemy.m_iInitX),
    m_iInitY(_RenderEnemy.m_iInitY),
    m_lAppearance(_RenderEnemy.m_lAppearance),
    m_lBeginBlink(_RenderEnemy.m_lBeginBlink)
{
    m_EnemyEntity = _RenderEnemy.GetEnemyBehaviour();
    m_PowerUpEntity = _RenderEnemy.GetPowerUpBehaviour();
}

//**************************
//Description : Destructor
//Parameters : None
//Note : None
//**************************
CRenderEnemy::~CRenderEnemy()
{
    if ( m_EnemyEntity != NULL )
        delete m_EnemyEntity;

    if ( m_PowerUpEntity != NULL )
        delete m_PowerUpEntity;
}

//**************************
//Description : Set the initial value for the rendering
//Parameters : The screen,
//             A pointer toward the enemy entity
//             its time before appearance
//             The PowerUp Behaviour (if any)
//              Animations for enemy, shots and powerup (if any)
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::SetRender(SDL_Surface * _screen,
                        CDefaultEnemyBehaviour * _Enemy, long _lAppearance,
                        CSpriteAnimation * _EnemyAnimations,
                        CSpriteAnimation * _EnemyShotsAnimation,
                        CDefaultPowerUpBehaviour * _PowerUp,
                        CSpriteAnimation * _PowerUpAnimation)
{
    m_Screen = _screen;

    SetSprites(_EnemyAnimations, _EnemyShotsAnimation);

    SetEnemyBehaviour(_Enemy, _lAppearance);

    if ( _PowerUp != NULL )
        SetPowerUpBehaviour(_PowerUp, _PowerUpAnimation );
}

//**************************
//Description : Initialise the rendering to its beginning
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::InitRender()
{
    m_EnemyEntity->ClearShots();
    m_EnemyEntity->SetPosition(m_iInitX, m_iInitY);
    m_EnemyEntity->RestoreLife();

    for (int i = 0; i < NUMBER_STATES; i++)
        m_vEntitySprites[i].RewindAnim();

    m_iState = STANDBY;

    if ( m_PowerUpEntity != NULL )
        m_bHasPowerUp = true;
}

//**************************
//Description : Return the time before the appearance of the enemy
//Parameters : None
//Return Value : The time of appearance
//Note : None
//**************************
long CRenderEnemy::GetAppearance() const
{
    return m_lAppearance;
}

//**************************
//Description : Set sprites for the rendering if it's not done
//Parameters : animations for the enemy
//              animations for its shots
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::SetSprites(CSpriteAnimation * _EnemyAnimations,
                            CSpriteAnimation * _EnemyShotsAnimation)
{
    IRenderEntity::SetSprites(_EnemyAnimations);

    //we set the animations of the shots
    m_vShotsAnimations = _EnemyShotsAnimation;
}


//**************************
//Description : Return the Enemy entity
//Parameters : None
//Return Value : The Enemy entity
//Note : None
//**************************
CDefaultEnemyBehaviour * CRenderEnemy::GetEnemyBehaviour() const
{
    return m_EnemyEntity;
}

//**************************
//Description : Set m_PlayerEntity if it's not done
//Parameters : A pointer toward an enemy and its time before appearance
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::SetEnemyBehaviour(CDefaultEnemyBehaviour * _Enemy, long _lAppearance)
{
    if ( m_EnemyEntity == NULL )
    {
        m_EnemyEntity = _Enemy;

        m_lAppearance = _lAppearance;

        //we set the initial position
        m_iInitX = m_EnemyEntity->GetX();
        m_iInitY = m_EnemyEntity->GetY();

        //we set the height and the width of the entity
        m_EnemyEntity->SetHeight(m_vEntitySprites[m_iState].GetHeight());
        m_EnemyEntity->SetWidth(m_vEntitySprites[m_iState].GetWidth());
    }
}

//**************************
//Description : Return the PowerUp entity
//Parameters : None
//Return Value : The PowerUp entity
//Note : None
//**************************
CDefaultPowerUpBehaviour * CRenderEnemy::GetPowerUpBehaviour() const
{
    return m_PowerUpEntity;
}

//**************************
//Description : Set m_PowerUpEntity if it's not done
//Parameters : The PowerUp Behaviour and the animation for the power up
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::SetPowerUpBehaviour(CDefaultPowerUpBehaviour * _PowerUp, CSpriteAnimation * _PowerUpAnimation)
{
    if ( m_PowerUpEntity == NULL )
    {
        //we create the sprites for the powerup
        m_vPowerUpSprite = new CSprite(_PowerUpAnimation, m_Screen);

        if ( m_vPowerUpSprite->IsBuilt() )
        {
            m_PowerUpEntity = _PowerUp;

            m_bHasPowerUp = true;

            //we set the height and the width of the entity
            m_PowerUpEntity->SetHeight(m_vPowerUpSprite->GetHeight());
            m_PowerUpEntity->SetWidth(m_vPowerUpSprite->GetWidth());
        }
    }
}

//**************************
//Description : Return the value of m_bHasPowerUp in order to know if the enemy has a power up or not
//Parameters : None
//Return Value : true if the enemy has a power up active, otherwise false
//Note : None
//**************************
bool CRenderEnemy::HasPowerUpBehaviour() const
{
    return m_bHasPowerUp;
}
//**************************
//Description : The player caught the power up, so the power up is now NULL
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::LosePowerUpBehaviour()
{
    m_bHasPowerUp = false;
}

//**************************
//Description : Render the Shot
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::RenderShots()
{
    //width and height and position of a shot animation
    int wSh, hSh, xSh, ySh;
    //sprite of a shot
    CSprite * SpriteShot;

    //we get the list of shots of the entity
    std::list<CDefaultShotBehaviour*> * ListShots = m_EnemyEntity->HasShots();

    //we erase all shots out of the screen
    m_EnemyEntity->PopShotOutOfScreen();

    SDL_Rect AreaDisplayed, Dest;

    //for all the shots
    for (std::list<CDefaultShotBehaviour *>::iterator it = ListShots->begin();
        it != ListShots->end();
        it++)
    {
        //we get the sprite
        SpriteShot = (*it)->GetSprite();

        //we get the height and width
        wSh = SpriteShot->GetWidth();
        hSh = SpriteShot->GetHeight();
        xSh = (*it)->GetX();
        ySh = (*it)->GetY();

        //we update the position
        (*it)->EntityTrajectory();

        //we draw it partially if it isn't completely in the screen
        if ( !(*it)->IsInTheScreen() )
        {
            Dest.x = ((xSh - wSh/2) > GAME_WINDOW_X) ? (xSh-wSh/2) : GAME_WINDOW_X;
            Dest.y = ((ySh - hSh/2) > 0) ? (ySh-hSh/2) : 0;

            AreaDisplayed.x = Dest.x - (xSh - wSh/2);
            AreaDisplayed.y = Dest.y - (ySh - hSh/2);
            AreaDisplayed.w = ( (xSh + wSh/2) >= (GAME_WINDOW_X + GAME_WINDOW_WIDTH) ) ?
                                (GAME_WINDOW_X + GAME_WINDOW_WIDTH - Dest.x) : ((xSh + wSh/2) - Dest.x);
            AreaDisplayed.h = ( (ySh + hSh/2) >= (GAME_WINDOW_HEIGHT) ) ?
                                (GAME_WINDOW_HEIGHT - Dest.y) : ((ySh + hSh/2) - Dest.y);

            //we set the position of the sprite
            SpriteShot->Set(xSh-wSh/2, ySh-hSh/2);

            //we draw it
            SpriteShot->Draw(&Dest, &AreaDisplayed);
        }
        else
        {
            //we set the position of the sprite
            SpriteShot->Set(xSh-wSh/2, ySh-hSh/2);

            //we draw it
            SpriteShot->Draw();
        }
    }
}

//**************************
//Description : Render the Entity
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::RenderEntity()
{
    //if the enemy is not dead, we draw it...
    if ( m_iState != DEAD )
    {
        //The entity shoot
        m_EnemyEntity->Shoot(m_vShotsAnimations, m_Screen);

        //we get the height and the width and position of the current animation
        int wSp = m_vEntitySprites[m_iState].GetWidth();
        int hSp = m_vEntitySprites[m_iState].GetHeight();
        int xSp = m_EnemyEntity->GetX();
        int ySp = m_EnemyEntity->GetY();


        //we draw it partially if it isn't completely in the screen
        if ( !m_EnemyEntity->IsInTheScreen() )
        {
            SDL_Rect AreaDisplayed, Dest;

            Dest.x = ((xSp - wSp/2) > GAME_WINDOW_X) ? (xSp-wSp/2) : GAME_WINDOW_X;
            Dest.y = ((ySp - hSp/2) > 0) ? (ySp-hSp/2) : 0;

            AreaDisplayed.x = Dest.x - (xSp - wSp/2);
            AreaDisplayed.y = Dest.y - (ySp - hSp/2);
            AreaDisplayed.w = ( (xSp + wSp/2) >= (GAME_WINDOW_X + GAME_WINDOW_WIDTH) ) ?
                                (GAME_WINDOW_X + GAME_WINDOW_WIDTH - Dest.x) : ((xSp + wSp/2) - Dest.x);
            AreaDisplayed.h = ( (ySp + hSp/2) >= (GAME_WINDOW_HEIGHT) ) ?
                                (GAME_WINDOW_HEIGHT - Dest.y) : ((ySp + hSp/2) - Dest.y);

            //we set the position of the enemy
            m_vEntitySprites[m_iState].Set(xSp-wSp/2, ySp-hSp/2);

            //we draw it
            m_vEntitySprites[m_iState].Draw(&Dest, &AreaDisplayed);
        }
        else
        {
            //we set the position of the enemy
            m_vEntitySprites[m_iState].Set(xSp-wSp/2, ySp-hSp/2);

            //we draw it
            m_vEntitySprites[m_iState].Draw();
        }


        if ( (m_iState == BLINK) && ( (Uint32)(m_lBeginBlink + BLINK_ENEMY_TIME) < SDL_GetTicks() ) )
        {
            m_iState = STANDBY;
        }
    }
    else
    {
        //the animation of the dead is not finished, we continue it
        if ( !m_vEntitySprites[m_iState].EndAnimation() )
        {
            //we don't have to set the trajectory because when the enemy die, it don't move anymore
            //we get the height and the width and position of the current animation
            int wSp = m_vEntitySprites[m_iState].GetWidth();
            int hSp = m_vEntitySprites[m_iState].GetHeight();
            int xSp = m_EnemyEntity->GetX();
            int ySp = m_EnemyEntity->GetY();


            //we draw it partially if it isn't completely in the screen
            if ( !m_EnemyEntity->IsInTheScreen() )
            {
                SDL_Rect AreaDisplayed, Dest;

                Dest.x = ((xSp - wSp/2) > GAME_WINDOW_X) ? (xSp-wSp/2) : GAME_WINDOW_X;
                Dest.y = ((ySp - hSp/2) > 0) ? (ySp-hSp/2) : 0;

                AreaDisplayed.x = Dest.x - (xSp - wSp/2);
                AreaDisplayed.y = Dest.y - (ySp - hSp/2);
                AreaDisplayed.w = ( (xSp + wSp/2) >= (GAME_WINDOW_X + GAME_WINDOW_WIDTH) ) ?
                                    (GAME_WINDOW_X + GAME_WINDOW_WIDTH - Dest.x) : ((xSp + wSp/2) - Dest.x);
                AreaDisplayed.h = ( (ySp + hSp/2) >= (GAME_WINDOW_HEIGHT) ) ?
                                    (GAME_WINDOW_HEIGHT - Dest.y) : ((ySp + hSp/2) - Dest.y);

                //we set the position of the enemy
                m_vEntitySprites[m_iState].Set(xSp-wSp/2, ySp-hSp/2);

                //we draw it
                m_vEntitySprites[m_iState].Draw(&Dest, &AreaDisplayed);
            }
            else
            {
                //we set the position of the enemy
                m_vEntitySprites[m_iState].Set(xSp-wSp/2, ySp-hSp/2);

                //we draw it
                m_vEntitySprites[m_iState].Draw();
            }
        }
        else
            //we set the y of the enemy higher than the window in order to not call render (waste of time)
            m_EnemyEntity->SetY(GAME_WINDOW_HEIGHT + 1000);

        //we draw the power up, if any and if it isn't out of the screen
        if ( m_bHasPowerUp && !((m_PowerUpEntity->GetY() - m_PowerUpEntity->GetHeight()/2) > GAME_WINDOW_HEIGHT) )
        {
            //we update the position
            m_PowerUpEntity->EntityTrajectory();

            //we get the height and the width and the position of the current animation
            int wSp = m_vPowerUpSprite->GetWidth();
            int hSp = m_vPowerUpSprite->GetHeight();
            int xSp = m_PowerUpEntity->GetX();
            int ySp = m_PowerUpEntity->GetY();

            //we draw it partially if it isn't completely in the screen
            if ( !m_PowerUpEntity->IsInTheScreen() )
            {
                SDL_Rect AreaDisplayed, Dest;

                Dest.x = ((xSp - wSp/2) > GAME_WINDOW_X) ? (xSp-wSp/2) : GAME_WINDOW_X;
                Dest.y = ((ySp - hSp/2) > 0) ? (ySp-hSp/2) : 0;

                AreaDisplayed.x = Dest.x - (xSp - wSp/2);
                AreaDisplayed.y = Dest.y - (ySp - hSp/2);
                AreaDisplayed.w = ( (xSp + wSp/2) >= (GAME_WINDOW_X + GAME_WINDOW_WIDTH) ) ?
                                    (GAME_WINDOW_X + GAME_WINDOW_WIDTH - Dest.x) : ((xSp + wSp/2) - Dest.x);
                AreaDisplayed.h = ( (ySp + hSp/2) >= (GAME_WINDOW_HEIGHT) ) ?
                                    (GAME_WINDOW_HEIGHT - Dest.y) : ((ySp + hSp/2) - Dest.y);

                //we set the position of the power up
                m_vPowerUpSprite->Set(xSp-wSp/2, ySp-hSp/2);

                //we draw it
                m_vPowerUpSprite->Draw(&Dest, &AreaDisplayed);
            }
            else
            {
                //we set the position of the power up
                m_vPowerUpSprite->Set(xSp-wSp/2, ySp-hSp/2);

                //we draw it
                m_vPowerUpSprite->Draw();
            }
        }
    }
}

//**************************
//Description : Render the entity and all things that have connections with it (shots of an entity, power up after death of an enemy, ...)
//Parameters : None
//Return Value : None
//Note : None
//**************************
void CRenderEnemy::Render()
{
    //we render if the screen is not null and if the entity is not out of the screen
    if ( m_Screen != NULL )
    {
        //if the entity is not dead and is not passed (so is Y is lower than the height of the screen), we update its position
        if ( m_iState != DEAD && ( m_EnemyEntity->GetY() - m_vEntitySprites[m_iState].GetHeight() / 2 ) <= GAME_WINDOW_HEIGHT )
            m_EnemyEntity->EntityTrajectory();

        //but we render the entity only if it's not out of the screen and if its power up is taken if the enemy is dead (if it has any to begin with)
        if ( ((m_iState == DEAD) && m_bHasPowerUp) || !m_EnemyEntity->IsOutOfScreen() )
            //we render the entity
            RenderEntity();

        //we render shots
        RenderShots();
    }
}

//**************************
//Description : Test if the player collide with the ennemy and/or its shots
//Parameters : The player
//Return Value : True if there is collision, false if not
//Note : None
//**************************
bool CRenderEnemy::CollideWithPlayer(CRenderPlayer * _Player)
{
    bool bCollision = false;

    CPlayerBehaviour * _PlayerBehaviour = _Player->GetPlayerBehaviour();

    //if the player or us are not dead we can test the collision
    if ( !_PlayerBehaviour->IsDead() && !m_EnemyEntity->IsDead() )
    {
        //if the player is not invulnerable
        if ( !_Player->IsExploding() && !_Player->IsResurecting() )
        {
            //We test the collision with the player if it isn't invulnerable
            bCollision = m_EnemyEntity->CollideWith( _PlayerBehaviour );

            //if there is collision, we lose a life
            if (bCollision)
                m_EnemyEntity->LoseLife();
        }

        std::list<CDefaultShotBehaviour *> * ListPlayerShots = _PlayerBehaviour->HasShots();

        std::list<CDefaultShotBehaviour *>::iterator it = ListPlayerShots->begin();

        std::list<CDefaultShotBehaviour *>::iterator end = ListPlayerShots->end();


        //we test collision with enemy shots, until we find that we are dead
        while (it != ListPlayerShots->end() && !m_EnemyEntity->IsDead() )
        {
            //we test the collision
            bCollision = (*it)->CollideWith( m_EnemyEntity );

            //if there is collision
            if (bCollision)
            {
                //we lose a life
                m_EnemyEntity->LoseLife((*it)->GetPowerShot());

                //we make the enemy blink
                m_iState = BLINK;

                //we save the beginning of the blinking
                m_lBeginBlink = SDL_GetTicks();

                //we delete the shot
                it = _PlayerBehaviour->PopShot(it);
            }
            else
                //we go to the next shot
                ++it;
        }

        //if we are dead
        if ( m_EnemyEntity->IsDead() )
        {
            //the player gain points and has an enemy killed more
            _PlayerBehaviour->AddPoints( m_EnemyEntity->GetPoints() );
            _PlayerBehaviour->AddEnemiesKilled();

            m_iState = DEAD;

            int wSp = m_vEntitySprites[m_iState].GetWidth();
            int hSp = m_vEntitySprites[m_iState].GetHeight();

            //we set the position of the sprite here, because it don't move anymore
            m_vEntitySprites[m_iState].Set(m_EnemyEntity->GetX()-wSp/2, m_EnemyEntity->GetY()-hSp/2);

            if ( m_PowerUpEntity != NULL )
            {
                m_PowerUpEntity->SetPosition(m_EnemyEntity->GetX(), m_EnemyEntity->GetY());
            }
        }
    }

    return bCollision;
}

//**************************
//Description : Test if the enemy is dead
//Parameters : None
//Return Value : True if the enemy is considered as dead, otherwise false
//Note : The enemy is considered has dead when its animation of death has finished and
//      When its powerup (if it has any) is caught or out of the screen
//**************************
bool CRenderEnemy::IsEnemyDead() const
{
    if ( m_iState == DEAD && m_vEntitySprites[m_iState].EndAnimation() )
    {
        //if the power up is at false or out of the screen, the enemy is dead
        return ( !m_bHasPowerUp || m_PowerUpEntity->IsOutOfScreen() );
    }

    return false;
}
